#pragma once

#include <unordered_map>
#include <vector>

#include "arithmetic.h"
#include "types.h"

namespace AST {
    class ASTNode;
}
class Symbol;

namespace Semantics {
    struct Info {
        Type::Basic baseType;

        template <class T>
        T* As() {
            return dynamic_cast<T*>(this);
        }

        virtual void Print(int dep) const = 0;

      protected:
        explicit Info(Type::Basic type) : baseType(type) {}
    };

    struct Void : public Info {
        void Print(int dep) const override;

        explicit Void() : Info(Type::Basic::Void) {}
    };

    struct Value : public Info {
        void Print(int dep) const override;

        explicit Value(Type::Basic type) : Info(type) {}
    };

    // Constant - can specify the value during compiling
    struct Constant : public Value {
        ArithValue value;

        explicit Constant(ArithValue value) : Value(value.type), value(value) {}
        void Print(int dep) const override;
    };

    // LValue - can be the left operand of '='
    struct LValue : public Value {
        ArithValue init;
        Symbol* symbol = nullptr;

        explicit LValue(Type::Basic type) : Value(type) { init.type = type; }
        void Print(int dep) const override;
    };

    // Pointer - used in array access and function arguments.
    // shape:
    //   int a[10]: shape = [];
    //   int a[5][10]: shape = [10];
    struct Pointer : public Info {
        std::vector<int> shape;
        std::unordered_map<int, ArithValue>* init = nullptr;
        Symbol* symbol = nullptr;

        explicit Pointer(Type::Basic type) : Info(type) {}
        explicit Pointer(Type::Basic type, const std::vector<int>& shape)
            : Info(type), shape(shape) {}
        void Print(int dep) const override;
    };

    struct Function : public Info {
        AST::ASTNode* func;
        std::vector<Type> argType;
        bool isExternal = false;
        bool hasVA = false;

        explicit Function(Type::Basic type) : Info(type) {}
        void Print(int dep) const override;
    };

}  // namespace Semantics
